home *** CD-ROM | disk | FTP | other *** search
- // isounit.cpp: Iso Class Implementation
-
- #include "keybrd.h"
- #include "isounit.h"
- #include "msmouse.h"
-
- MsgPkt NullMsg = { NULL, Idle, Idle, 0, 0, "" };
-
- Iso::Iso(Fso *P)
- // Initializes the Iso by setting all of the instance variables to
- // a default state and sets up an IsoMgr object
- {
- Panel = P; // P is either a Tfso or Gfso
- Active = False; // Not selected
- Visible = False; // Object is not visible
- IsClosed = True; // Object is not open
- TouchFlag = False; // Object is not touching another Iso
- ClipToFrame = False; // Normally, clip to the interior
- Under = NULL; Over = NULL; // The Iso stack is empty
- Base = NULL; // No base to start with
- SubMgr = new IsoMgr(this); // Make an IsoMgr for this object
- }
-
- Iso::~Iso(void)
- // De-allocates memory for an Iso by first removing the object
- // from its Base's Iso stack and it destroys its own stack
- {
- Remove(); // Hide() & remove from Iso stack
- delete SubMgr;
- delete Panel;
- }
-
- Rso *Iso::ClippingRect(void)
- // Given the state of the ClipToFrame flag, returns the
- // appropriate clipping rectangle
- {
- if (ClipToFrame)
- return Base->Panel->Frame;
- else return Base->Panel->Interior;
- }
-
- void Iso::SetLocn(int Xl, int Yl, CoordType Ctype)
- // Sets the location of the Iso. If Ctype == Relc, the
- // coordinates are relative to the object's base. If the
- // object does not have a base, (like for FullScrn), then
- // it's assumed the coordinates are always absolute, and
- // that SetLocn is only called once.
- {
- int Dx, Dy, Dw, Dh;
- Iso *P;
- Rso *Clipper;
-
- if (Base == NULL) { // Object does not have a base
- Panel->SetLocn(Xl, Yl); // (Like FullScrn)
- }
- else { // Object has a base
- Clipper = ClippingRect();
- if (Ctype == Relc) { // Use relative coords
- Xl += Clipper->Xul; // Turn into absolute coords
- Yl += Clipper->Yul; // Turn into absolute coords
- }
- // Make sure coordinates are in range. It's assumed that
- // the size is OK at this point.
- Dw = Panel->Frame->Wd; Dh = Panel->Frame->Ht;
- Clipper->ClipDim(Xl, Yl, Dw, Dh);
- // Record distance to move for all dependent objects
- Dx = Xl - Panel->Frame->Xul;
- Dy = Yl - Panel->Frame->Yul;
- // Change location of the object
- Panel->SetLocn(Xl, Yl);
- // Change locations of all dependent objects
- P = SubMgr->Top;
- while (P != NULL) {
- P->SetLocn(P->Panel->Frame->Xul+Dx, P->Panel->Frame->Yul+Dy, Absc);
- P = P->Under; // Get next object on stack
- }
- }
- }
-
- void Iso::SetSize(int W, int H)
- // The size of an Iso is the size of it's panel. W and H are
- // the new size of the interior. First, we set the size of
- // the panel, which will set the sizes of the interior, frame,
- // and overall rectangles. Then, if we have a base, we clip the
- // frame size to it. If the frame size changes during clipping,
- // we re-size the panel w.r.t interior again.
- {
- int Dw, Dh, Ow, Oh;
- Rso *Clipper;
- Rso *Pf = Panel->Frame;
-
- Panel->SetSize(W, H); // First set up panel size
- if (Base != NULL) { // Do we have a base to clip to?
- Clipper = ClippingRect();
- Pf = Panel->Frame;
- Ow = Pf->Wd; Oh = Pf->Ht; // Record old frame size
- Clipper->ClipSize(Pf->Wd, Pf->Ht); // Adjust frame size
- Dw = Pf->Wd - Ow; Dh = Pf->Ht - Oh; // Compute delta size
- if ((Dw != 0) || (Dh != 0)) {
- // Need to set the size of the panel again
- Panel->SetSize(Panel->Interior->Wd+Dw, Panel->Interior->Ht+Dh);
- }
- }
- }
-
- void Iso::DrawPanel(void)
- // First, initializes the swap buffer by calling GetImage,
- // draws the frame, draws the shadows, clears the
- // interior, and calls the specialized draw routine.
- {
- Mouse.Hide();
- Visible = True; // We'll be visible soon
- if (Panel->IsSwappable()) Panel->GetImage(ClippingRect());
- Panel->DrawFrame(0, 0);
- Panel->DrawShadows(ClippingRect(), GetIm, 1);
- Panel->Clear(' ', 0);
- Draw();
- Mouse.Show();
- }
-
- void Iso::Open(Iso *B, int X, int Y)
- // Opens the object by drawing it on a specified base at
- // position X,Y relative to the base. Since we now have a
- // base, we also clip the size w.r.t to the base. (It's assumed
- // that SetSize has already been called at least once.)
- {
- Base = B;
- // Push the object onto its base managers' stack
- Base->SubMgr->Push(this);
- IsClosed = False; // Object now to be open
- // Adjust size if necessary. Can do this by calling SetSize,
- // which will in turn call ClipSize.
- SetSize(Panel->Interior->Wd, Panel->Interior->Ht);
- SetLocn(X, Y, Relc); // Use relative coordinates
- DrawPanel(); // Draw an empty panel
- }
-
- void Iso::Reopen(int X, int Y)
- // Re-opens an object that was previously closed. The new
- // location (X,Y) is relative to the base
- {
- IsClosed = False;
- SetLocn(X, Y, Relc);
- Select(); // Move Iso to front and show
- }
-
- void Iso::Move(int X, int Y)
- // Moves the object to new location. Absolute coordinates
- // are used.
- {
- if (Panel->IsSwappable()) { // Only swappable objects
- Hide(); // can be moved
- SetLocn(X, Y, Absc);
- Show();
- }
- }
-
- void Iso::DeltaMove(int Dx, int Dy)
- // Moves the object by an incremental amount
- {
- // Add in the object's origin
- Move(Panel->Frame->Xul+Dx, Panel->Frame->Yul+Dy);
- }
-
- void Iso::MoveLoop(MsgPkt &M)
- // Moves an object until mouse button is released
- {
- unsigned E;
- Mouse.Moved(); // Resets counters
- do {
- if (Mouse.Moved()) DeltaMove(Mouse.Dx, Mouse.Dy);
- E = Mouse.Event(M.Mx, M.My);
- } while (E != MouseUp);
- M.RtnCode = Idle;
- }
-
- void Iso::Stretch(int W, int H)
- // Stretch to a new size. First, closes all sub-windows and figure
- // out the minimum size we can stretch to, hides this object, does
- // the resize, and redraws everybody.
- {
- Iso *P;
- int Mw, Mh;
-
- if (Panel->IsStretchable()) {
- // Hide the subiso's. While we're at it, find maximum
- // size of subiso's, which serves as our minimum.
- Mw = 8; Mh = 3; // Absolute minimums
- P = SubMgr->Top;
- while (P != NULL) { // Hide all sub-windows
- if (P->Panel->Frame->Wd > Mw) Mw = P->Panel->Frame->Wd;
- if (P->Panel->Frame->Ht > Mh) Mh = P->Panel->Frame->Ht;
- P->Hide();
- P = P->Under;
- }
- if (W < Mw) W = Mw; // Adjust sizes to lower bound
- if (H < Mh) H = Mh;
- Hide(); // Hide image
- SetSize(W, H);
- // This next call is to relocate the shadows
- SetLocn(Panel->Frame->Xul, Panel->Frame->Yul, Absc);
- Redraw();
- P = SubMgr->Bottom;
- while (P != NULL) { // Reshow the sub-windows
- P->SetLocn(P->Panel->Frame->Xul, P->Panel->Frame->Yul, Absc);
- if (!P->IsClosed) P->Show();
- P = P->Over;
- }
- }
- }
-
- void Iso::DeltaStretch(int Dw, int Dh)
- // Do an incremental stretch
- {
- Stretch(Panel->Interior->Wd+Dw, Panel->Interior->Ht+Dh);
- }
-
- void Iso::StretchLoop(MsgPkt &M)
- // Stretches the window until a MouseUp event
- {
- unsigned E;
- Mouse.Moved(); // Resets counters
- do {
- if (Mouse.Moved()) DeltaStretch(Mouse.Dx, Mouse.Dy);
- E = Mouse.Event(M.Mx, M.My);
- } while (E != MouseUp);
- M.RtnCode = Idle;
- }
-
- void Iso::Swap(void)
- // Swap between the object's save buffer and the screen. The
- // shadow clipping region is set to the base's interior, unless
- // the Iso has no base, it's set to the Iso's overall rect.
- {
- XfrDirn Xd;
- if (Visible) Xd = PutIm; else Xd = GetIm;
- if (Base != NULL)
- Panel->Swap(Base->Panel->Interior, Xd);
- else Panel->Swap(Panel->Overall, Xd);
- }
-
- void Iso::Hide(void)
- // Hides the object by swapping images
- {
- if (IsClosed) return; // Don't hide if already closed
- if (Visible) { // or already hidden
- Swap(); // Swap out the object
- SetVisibleFlag(False); // Recursively reset visible flags
- }
- }
-
- void Iso::Show(void)
- // Shows the object by swapping images
- {
- if (IsClosed) return; // Don't show if closed
- if (!Visible) { // or already visible
- Swap();
- SetVisibleFlag(True); // Recursively set visible flags
- }
- }
-
- void Iso::SetVisibleFlag(int F)
- // Recursively set/reset the visible flags of this
- // object and all of its children
- {
- Iso *P;
- if (IsClosed) return;
- P = SubMgr->Top; // Start at the top of the stack
- while (P != NULL) {
- P->SetVisibleFlag(F);
- P = P->Under;
- }
- Visible = F;
- }
-
- void Iso::Select(void)
- // Selects an object to be the active screen object. When
- // the object is selected it is brought to the front.
- {
- if (Base != NULL) {
- Base->Select(); // Make sure parent is selected first
- // Then, select yourself
- Base->SubMgr->MoveToFront(this, True);
- }
- Show(); // Make sure it's showing
- }
-
- void Iso::Remove(void)
- // Removes the Iso by moving it to the front of the stack, and
- // detaching it from the stack. Set base to NULL to indicate
- // it no longer belongs to a stack.
- {
- if (Base != NULL) Base->SubMgr->MoveToFront(this, False);
- Base = NULL;
- }
-
- void Iso::Prompt(void)
- // Default prompt action is to select the object, and
- // set the Active flag
- {
- Select(); Active = True;
- }
-
- void Iso::UnPrompt(void)
- // Default unprompt action is to merely set the Active
- // flag to false
- {
- Active = False;
- }
-
- void Iso::SwitchFocus(MsgPkt &M)
- // Switch the focus to Self by first leaving the old focus
- // (if there was one), and entering Self. Don't need to
- // do anything if this object is already the focus
- {
- if (M.Focus != this) { // Nothing to do
- if (M.Focus != NULL) {
- M.Focus->Leave(M); // Leave old focus
- }
- M.Focus = this; // Enter this object
- Enter(M);
- }
- M.RtnCode = Idle;
- }
-
- void Iso::Enter(MsgPkt &)
- // Default Enter action is to prompt the object if
- // not already active
- {
- if (!Active) Prompt();
- }
-
- void Iso::Leave(MsgPkt &M)
- // Unprompts the object if it is active. If you are the
- // current focus, you MUST set the focus to nil
- {
- if (Active) UnPrompt();
- if (M.Focus == this) M.Focus = NULL;
- }
-
- // --------------------- Mouse event methods ----------------
-
- void Iso::OnMouseEnter(MsgPkt &M)
- // If mouse button is down when the mouse enters this
- // object, switch focus to this object
- {
- if (Mouse.ButtonStatus() != 0) SwitchFocus(M);
- }
-
- void Iso::OnMouseLeave(MsgPkt &M)
- // If mouse button is down when the mouse leaves this
- // object, then leave this object
- {
- if (Mouse.ButtonStatus() != 0) Leave(M);
- }
-
- void Iso::OnClose(MsgPkt &M)
- // On a Close event, hide this object and set it's closed flag
- {
- Leave(M);
- // If if statement below traps the fullscrn case too
- if (Panel->IsCloseable()) {
- Hide();
- IsClosed = True;
- }
- }
-
-
- void Iso::OnMouseDown(MsgPkt &M)
- // If mouse button pressed while inside this object,
- // then switch focus to it, handle possibility of
- // being on the border
- {
- SwitchFocus(M);
- if (Panel->OnBorder(M.Mx, M.My)) BorderHandler(M);
- }
-
- void Iso::BorderHandler(MsgPkt &M)
- // Handle border conditions: either activate close button,
- // stretch, or move the object. Stretching occurs if on
- // lower right-hand corner, UNLESS the window is only
- // 1x1 in size (eg. scroll buttons.)
- {
- if (Panel->OnCloseButton(M.Mx, M.My)) {
- M.RtnCode = Close;
- M.Focus= this;
- }
- else if ((M.Mx == Panel->Frame->Xlr) &&
- (M.My == Panel->Frame->Ylr) &&
- // These tests are so we don't cause Move() to
- // to fail on Iso's like scroll buttons
- (Panel->Frame->Wd > 1) &&
- (Panel->Frame->Ht > 1))
- {
- StretchLoop(M);
- }
- else {
- MoveLoop(M);
- }
- }
-
- // --------- Methods to process keyboard events --------
-
- void Iso::OnKeyStroke(MsgPkt &M)
- // Process all key press events. Default is to handle only
- // the return key, and the shift arrow keys
- {
- switch (M.Code) {
- case CrKey:
- Activate(M);
- M.Code = Idle;
- break;
- default :
- if (IsShiftArrow(M.Code)) {
- OnShiftArrow(M);
- M.Code = Idle;
- }
- }
- }
-
- void Iso::OnShiftArrow(MsgPkt &M)
- // The shift arrow key events cause the window to move
- {
- switch (M.Code) {
- case ShiftLeft : DeltaMove(-1, 0); break;
- case ShiftRight : DeltaMove(1, 0); break;
- case ShiftUpKey : DeltaMove(0, -1); break;
- case ShiftDnKey : DeltaMove(0, 1); break;
- default: ;
- }
- }
-
- void Iso::Dispatch(MsgPkt &M)
- // Dispatches the events to the appropriate method
- {
- M.RtnCode = Idle; // Default new message is to "idle"
- switch (M.Code) {
- case Idle : break;
- case StrMsg : break;
- case Close : OnClose(M); break;
- case MouseDown : OnMouseDown(M); break;
- case MouseStillDown : OnMouseStillDown(M); break;
- case MouseUp : OnMouseUp(M); break;
- case MouseEnter : OnMouseEnter(M); break;
- case MouseLeave : OnMouseLeave(M); break;
- case MouseWithin : OnMouseWithin(M); break;
- default: ;
- OnKeyStroke(M);
- }
- }
-
- int Iso::Obscured(void)
- // Returns true if Self is partially hidden by object from above
- {
- Iso *Ip = Over;
- while (Ip != NULL) {
- if (Panel->Touches(Ip->Panel)) {
- return True;
- }
- Ip = Ip->Over;
- }
- return False;
- }
-
- // ----------------------- IsoMgr Methods -----------------------
-
- IsoMgr::IsoMgr(Iso *B)
- // Initializes the IsoMgr object by setting the top and bottom
- // stack pointers to NULL, and recording who's the base.
- {
- Top = NULL; Bottom = NULL;
- Base = B;
- Hot = NULL;
- Marker = NULL;
- }
-
- IsoMgr::~IsoMgr(void)
- // Destroys all Iso's on the stack
- {
- Iso *P, *Q;
- P = Top;
- while (P != NULL) {
- Q = P->Under;
- delete P;
- P = Q;
- }
- }
-
- void IsoMgr::Push(Iso *Ip)
- // Attachs the Iso by pushing onto the Iso stack
- {
- if (Top != NULL) {
- Top->Over = Ip; // Link top of stack to new node
- Ip->Under = Top; // Link new node to top of stack
- }
- else { // First one on the stack
- Ip->Over = NULL;
- Ip->Under = NULL;
- Bottom = Ip; // Make it the bottom too
- Marker = Ip; // Set the marker on it
- }
- Top = Ip; // Set top of stack to new node
- }
-
- void IsoMgr::MoveToFront(Iso *Me, int Keep)
- // If Keep == 1, MoveToFront moves Me to the top of the stack
- // and makes it the active Iso.
- // If Keep == 0, Me is removed from the stack and hidden. Note
- // that nothing happens if Me is already at the top and we wish
- // to keep it there. Also, if not keeping Me, it is simply detached
- // from the stack, it is not destroyed!
- {
- Iso *Ip;
-
- if (Keep && (Me == Top)) return; // Short circuit
- Mouse.Hide();
- // If Me is an overlapping Iso, move its image to the top
- if (Me->Panel->IsSwappable()) { // Must erase image at old posn
- ResetTouchFlags(Me);
- SetTouchFlags(Me);
- if (!Me->TouchFlag) { // Nobody overlaps from above
- if (!Keep) Me->Hide(); // Erase image if deleting
- }
- else { // Somebody overlaps from above, so ...
- // Swap iso's in descending order, downto me
- Ip = Top;
- while (Ip != Me->Under) {
- if (Ip->TouchFlag) Ip->Hide();
- Ip = Ip->Under;
- }
- // Put Iso images (only those above me) back
- Ip = Me->Over;
- while (Ip != NULL) {
- if (Ip->TouchFlag) Ip->Show();
- Ip = Ip->Over;
- }
- }
- }
- // Through processing overlapping windows so
- // link up window underneath Me with the Iso above it
- // Note: if Me == Top, it is also true that we're
- // deleting Me
-
- if (Me == Top) { // We know we're deleting
- if (Me == Bottom) { // Stack will be empty
- Bottom = NULL;
- Top = NULL;
- }
- else { // Iso underneath to become new top
- Me->Under->Over = NULL;
- Top = Me->Under;
- }
- Me->Under = NULL; // Reset under/over pointers
- Me->Over = NULL;
- }
- else {
- // Me is not top, and we may or may not be deleting
- // at this point, we know we have at least two Iso's on stack
- if (Me == Bottom) { // We're getting a new bottom
- Me->Over->Under = NULL;
- Bottom = Me->Over;
- }
- else { // We're somewhere in the middle
- Me->Under->Over = Me->Over;
- Me->Over->Under = Me->Under;
- }
- if (Keep) { // Want to make me the new top iso
- Top->Over = Me;
- Me->Under = Top;
- Me->Over = NULL;
- Top = Me;
- if (Me->TouchFlag) Me->Show(); // Put back image
- }
- else { // Reset under/over pointers
- Me->Under = NULL; Me->Over = NULL;
- }
- }
- Mouse.Show();
- }
-
- Iso *CycleToSibling(Iso *Curr)
- // An auxiliary recursive function of CycleForw that find's
- // a sibling to cycle to. Note: This is not a method!
- {
- Iso *I;
-
- I = Curr->Base; // Is there a base ?
- if (I == NULL) { // No, so we don't belong to a stack
- I = Curr; // so about all we can do is stay put
- }
- else { // We belong to a stack
- I = Curr->Base->SubMgr->Bottom; // Possibly our sibling
- // Scan for first visible sibling, but stop if you hit marker
- while ((I != NULL) && (!I->Visible) &&
- (I != Curr->Base->SubMgr->Marker)) I = I->Over;
- // If no sibling, or we've already been there ...
- if ((I == NULL) || (I == Curr->Base->SubMgr->Marker)) {
- I = CycleToSibling(Curr->Base); // Try going forward from base
- } // Else we'll take i
- }
- return I;
- }
-
- Iso *IsoMgr::CycleForw(Iso *Curr)
- // Cycle forward through the stacks looking for someone
- // to become the new focus
- {
- Iso *I;
-
- if (Curr == NULL) return NULL;
- I = Curr->SubMgr->Bottom; // I == first child
- // Scan for a visible child
- while ((I != NULL) && (!I->Visible)) I = I->Over;
- // If no child, try to go to sibling
- if (I == NULL) I = CycleToSibling(Curr);
- return I;
- }
-
- void IsoMgr::ProcessCycle(MsgPkt &M)
- // A TabKey event means to cycle forward. A ShiftTabKey means
- // to cycle backwards (currently not implemented).
- {
- Iso *NewIso;
- if ((M.Code == TabKey) || (M.Code == ShiftTabKey)) {
- if (M.Focus == NULL) {
- if ((Bottom != NULL) && Bottom->Visible) {
- Bottom->SwitchFocus(M);
- }
- }
- else {
- switch(M.Code) {
- case TabKey :
- NewIso = CycleForw(M.Focus);
- if (NewIso != NULL) NewIso->SwitchFocus(M);
- break;
- case ShiftTabKey :
- // Cycle backwards ... not currently implemented
- break;
- default: ;
- }
- }
- }
- }
-
- void IsoMgr::ResetTouchFlags(Iso *Me)
- {
- while (Me != NULL) {
- Me->TouchFlag = False;
- Me = Me->Over;
- }
- }
-
- void IsoMgr::SetTouchFlags(Iso *Me)
- // Sets touch flags for all Iso's above Me that touch
- // Me. Must do this recursively for all touching Iso's.
- // Note that hidden Iso's are not included!
- {
- Iso *Ip;
-
- Ip = Me->Over;
- while (Ip != NULL) {
- if (Ip->Visible) { // Must be visible to be touching
- // The swappable check prevents flickering
- if ((Ip->Panel->IsSwappable()) &&
- Me->Panel->Touches(Ip->Panel)) {
- Me->TouchFlag = True;
- Ip->TouchFlag = True;
- SetTouchFlags(Ip); // Must do this!!
- }
- }
- Ip = Ip->Over;
- }
- }
-
- void IsoMgr::OnIso(int Mx, int My, Iso **I)
- // Recursive routine that finds the highest Iso which contains
- // the coordinates Mx, and My. It looks through the sub-stack
- // first. If it can't find anyone there, it tries the
- // Base Iso. Note that hidden Iso's are not included!
- {
- Iso *P, *Q;
- int Found;
-
- P = Top; Found = False;
- while ((P != NULL) && (!Found)) {
- // Iso has to be visible to be elgible
- if (P->Visible && P->Panel->OnFrame(Mx, My)) {
- Found = True;
- P->SubMgr->OnIso(Mx, My, &Q);
- if (Q != NULL) P = Q;
- }
- else {
- P = P->Under;
- }
- }
- if ((P == NULL) && (Base != NULL)) {
- if (Base->Visible && Base->Panel->OnFrame(Mx, My))
- P = Base;
- }
- *I = P;
- }
-
- void IsoMgr::EventLoop(MsgPkt &M)
- // Loop through the event handler until either a Shutdown
- // event (Alt-X) occurs, or there are no Iso's on the stack
- {
- Iso *P;
-
- do {
- EventStep(M);
- M.Code = M.RtnCode;
- P = Top;
- while ((P != NULL) && (!P->Visible)) P = P->Under;
- } while ((M.Code != ShutDown) && (P != NULL));
- }
-
- void IsoMgr::EventStep(MsgPkt &M)
- // First, handle any p}ing events, look for new ones.
- // Also, monitor the mouse movement between Iso's.
- {
- Iso *HotIso;
- unsigned E;
-
- // Handle any pending messages
- HotIso = NULL; // Very important to do this!
- if ((M.Code != Idle) && (M.Focus != NULL)) {
- M.Focus->Dispatch(M); // Note: the focus might change here
- }
- else { // Handle any key strokes
- E = KeyEvent();
- if (E != Idle) {
- M.Code = E;
- ProcessCycle(M); // Process possible cycle keys
- }
- else { // Handle any mouse activity
- E = Mouse.Event(M.Mx, M.My);
- OnIso(M.Mx, M.My, &HotIso);
- if (HotIso != Hot) {
- if (Hot != NULL) Hot->OnMouseLeave(M);
- if (HotIso != NULL) HotIso->OnMouseEnter(M);
- }
- Hot = HotIso;
- }
- if (E != Idle) {
- if (HotIso != M.Focus) {
- if (M.Focus != NULL) M.Focus->OnMouseLeave(M);
- if (HotIso != NULL) HotIso->OnMouseEnter(M);
- }
- }
- else if (M.Focus != NULL) E = MouseWithin;
- M.RtnCode = E; // Key/mouse event becomes new message for Focus
- }
- }
-
-